// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraWeaponStateComponent.h"

#include "Abilities/GameplayAbilityTargetTypes.h"
#include "Equipment/LyraEquipmentManagerComponent.h"
#include "GameFramework/Pawn.h"
#include "GameplayEffectTypes.h"
#include "Kismet/GameplayStatics.h"
#include "NativeGameplayTags.h"
#include "Physics/PhysicalMaterialWithTags.h"
#include "Teams/LyraTeamSubsystem.h"
#include "Weapons/LyraRangedWeaponInstance.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraWeaponStateComponent)

// 定义游戏标签：游戏区域
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Gameplay_Zone, "Gameplay.Zone");

/**
 * ULyraWeaponStateComponent 构造函数。
 * @param ObjectInitializer 对象初始化器
 */
ULyraWeaponStateComponent::ULyraWeaponStateComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// 默认启用复制
	SetIsReplicatedByDefault(true);

	// 启用Tick
	PrimaryComponentTick.bStartWithTickEnabled = true;
	PrimaryComponentTick.bCanEverTick = true;
}

/**
 * 组件Tick函数。
 */
void ULyraWeaponStateComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// 获取Pawn并检查装备管理器
	if (APawn* Pawn = GetPawn<APawn>())
	{
		if (ULyraEquipmentManagerComponent* EquipmentManager = Pawn->FindComponentByClass<ULyraEquipmentManagerComponent>())
		{
			// 获取当前远程武器实例并调用其Tick函数
			if (ULyraRangedWeaponInstance* CurrentWeapon = Cast<ULyraRangedWeaponInstance>(EquipmentManager->GetFirstInstanceOfType(ULyraRangedWeaponInstance::StaticClass())))
			{
				CurrentWeapon->Tick(DeltaTime);
			}
		}
	}
}

/**
 * 检查是否应将命中显示为成功。
 * @param Hit 命中结果
 * @return 如果应显示为成功返回true
 */
bool ULyraWeaponStateComponent::ShouldShowHitAsSuccess(const FHitResult& Hit) const
{
	AActor* HitActor = Hit.GetActor();

	//@TODO: 不要将未造成伤害的命中（由于无敌或类似原因）视为成功
	UWorld* World = GetWorld();
	// 使用团队子系统检查是否可以造成伤害
	if (ULyraTeamSubsystem* TeamSubsystem = UWorld::GetSubsystem<ULyraTeamSubsystem>(GetWorld()))
	{
		return TeamSubsystem->CanCauseDamage(GetController<APlayerController>(), Hit.GetActor());
	}

	return false;
}

/**
 * 检查是否应更新造成伤害时间。
 * @param EffectContext 游戏效果上下文
 * @return 如果应更新时间返回true
 */
bool ULyraWeaponStateComponent::ShouldUpdateDamageInstigatedTime(const FGameplayEffectContextHandle& EffectContext) const
{
	//@TODO: 实现我，为此组件的目的是，我们只关心由武器造成的伤害
	// 或从武器发射的抛射物造成的伤害，并应过滤到那个
	// （或者可能查看原因者是否也是我们活动准星配置的源）
	return EffectContext.GetEffectCauser() != nullptr;
}

/**
 * 客户端确认目标数据实现。
 */
void ULyraWeaponStateComponent::ClientConfirmTargetData_Implementation(uint16 UniqueId, bool bSuccess, const TArray<uint8>& HitReplaces)
{
	// 查找匹配的未确认命中标记批次
	for (int i = 0; i < UnconfirmedServerSideHitMarkers.Num(); i++)
	{
		FLyraServerSideHitMarkerBatch& Batch = UnconfirmedServerSideHitMarkers[i];
		if (Batch.UniqueId == UniqueId)
		{
			// 如果成功且命中替换数量与标记数量不匹配
			if (bSuccess && (HitReplaces.Num() != Batch.Markers.Num()))
			{
				UWorld* World = GetWorld();
				bool bFoundShowAsSuccessHit = false;

				int32 HitLocationIndex = 0;
				// 遍历批次中的所有标记
				for (const FLyraScreenSpaceHitLocation& Entry : Batch.Markers)
				{
					// 检查是否未被替换且显示为成功
					if (!HitReplaces.Contains(HitLocationIndex) && Entry.bShowAsSuccess)
					{
						// 只需要执行一次
						if (!bFoundShowAsSuccessHit)
						{
							ActuallyUpdateDamageInstigatedTime();
						}

						bFoundShowAsSuccessHit = true;

						// 添加到最后武器伤害屏幕位置
						LastWeaponDamageScreenLocations.Add(Entry);
					}
					++HitLocationIndex;
				}
			}

			// 移除已处理的批次
			UnconfirmedServerSideHitMarkers.RemoveAt(i);
			break;
		}
	}
}

/**
 * 添加未确认的服务器端命中标记。
 */
void ULyraWeaponStateComponent::AddUnconfirmedServerSideHitMarkers(const FGameplayAbilityTargetDataHandle& InTargetData, const TArray<FHitResult>& FoundHits)
{
	// 创建新的未确认命中标记批次
	FLyraServerSideHitMarkerBatch& NewUnconfirmedHitMarker = UnconfirmedServerSideHitMarkers.Emplace_GetRef(InTargetData.UniqueId);

	// 获取玩家控制器
	if (APlayerController* OwnerPC = GetController<APlayerController>())
	{
		// 为每个命中结果创建屏幕空间命中位置
		for (const FHitResult& Hit : FoundHits)
		{
			FVector2D HitScreenLocation;
			// 将世界位置投影到屏幕空间
			if (UGameplayStatics::ProjectWorldToScreen(OwnerPC, Hit.Location, /*out*/ HitScreenLocation, /*bPlayerViewportRelative=*/ false))
			{
				FLyraScreenSpaceHitLocation& Entry = NewUnconfirmedHitMarker.Markers.AddDefaulted_GetRef();
				Entry.Location = HitScreenLocation;
				Entry.bShowAsSuccess = ShouldShowHitAsSuccess(Hit);

				// 确定命中区域
				FGameplayTag HitZone;
				// 检查物理材质标签
				if (const UPhysicalMaterialWithTags* PhysMatWithTags = Cast<const UPhysicalMaterialWithTags>(Hit.PhysMaterial.Get()))
				{
					for (const FGameplayTag MaterialTag : PhysMatWithTags->Tags)
					{
						// 查找游戏区域标签
						if (MaterialTag.MatchesTag(TAG_Gameplay_Zone))
						{
							Entry.HitZone = MaterialTag;
							break;
						}
					}
				}
			}
		}
	}
}

/**
 * 更新造成伤害时间。
 */
void ULyraWeaponStateComponent::UpdateDamageInstigatedTime(const FGameplayEffectContextHandle& EffectContext)
{
	if (ShouldUpdateDamageInstigatedTime(EffectContext))
	{
		ActuallyUpdateDamageInstigatedTime();
	}
}

/**
 * 实际更新造成伤害时间。
 */
void ULyraWeaponStateComponent::ActuallyUpdateDamageInstigatedTime()
{
	// 如果我们的LastWeaponDamageInstigatedTime不是很近，清除LastWeaponDamageScreenLocations数组
	UWorld* World = GetWorld();
	if (World->GetTimeSeconds() - LastWeaponDamageInstigatedTime > 0.1)
	{
		LastWeaponDamageScreenLocations.Reset();
	}
	// 更新最后造成伤害时间
	LastWeaponDamageInstigatedTime = World->GetTimeSeconds();
}

/**
 * 获取自上次命中通知以来经过的时间。
 * @return 返回经过的时间（秒）
 */
double ULyraWeaponStateComponent::GetTimeSinceLastHitNotification() const
{
	UWorld* World = GetWorld();
	return World->TimeSince(LastWeaponDamageInstigatedTime);
}